﻿namespace MetaProperties.Core
{
    using System;

    using MetaProperties.Framework;

    /// <summary>
    /// An observable value that can be written exactly once.
    /// </summary>
    /// <typeparam name="T">The type of the contained value.</typeparam>
    public class WriteOnceObservable<T> : TypedObservable<T>, MetaProperties.Framework.IObservable<T>
    {
        /// <summary>
        /// The contained value.
        /// </summary>
        private T value;

        /// <summary>
        /// True if the value has been set; false otherwise.
        /// </summary>
        private bool isSet;

        /// <summary>
        /// Initializes a new instance of the <see cref="WriteOnceObservable&lt;T&gt;"/> class.
        /// </summary>
        public WriteOnceObservable()
        {
#if ANCESTRY_SUPPORTED
            this.UpdateValueParent(value, UpdateParentOperation.Add);
#endif
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="WriteOnceObservable&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="value">The value.</param>
        public WriteOnceObservable(T value)
            : this()
        {
            this.value = value;
            this.isSet = true;
        }

        /// <summary>
        /// Gets or sets the value of the observable property.
        /// </summary>
        /// <value>The contained value.</value>
        public T Value
        {
            get
            {
                return this.value;
            }

            set
            {
                if (this.isSet)
                {
                    throw new InvalidOperationException("Value has already been set.");
                }

                this.OnValueChanging();

                this.isSet = true;
                this.value = value;

                this.OnValueChanged(default(T), value);
            }
        }

        /// <summary>
        /// Gets a value indicating whether the value has been set.
        /// </summary>
        /// <value><c>true</c> if the value has been set; otherwise, <c>false</c>.</value>
        public bool IsSet
        {
            get { return this.isSet; }
        }

        /// <summary>
        /// Gets the value.
        /// </summary>
        /// <returns>The contained value.</returns>
        protected override object GetValue()
        {
            return this.Value;
        }
    }
}
